/* QtLua -- Lua bindings for Qt
   Copyright (C) 2011, Jarek Pelczar

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA

*/

#include "qluametaobjectwrapper_p.h"
#include "qluaengineglobals_p.h"
#include <QDebug>

namespace QLuaInternal {

void QLuaMetaObjectWrapper::registerModule(QLuaScriptEngine * engine)
{
    Q_ASSERT_X(engine != NULL, "QLuaMetaObjectWrapper::registerModule", "Called from incorrect context");
    QLuaScriptEnginePrivate * priv = QLuaScriptEnginePrivate::priv(engine);
    lua_State * L = priv->L;

    lua_getglobal(L, "QtLua");

    lua_newtable(L);

    lua_pushcclosure(L, dispatch_Gc, 0);
    lua_setfield(L, -2, "__gc");
    lua_pushcclosure(L, dispatch_Index, 0);
    lua_setfield(L, -2, "__index");

    lua_setfield(L, -2, "QLuaMetaObjectWrapper__metatable");

    lua_pop(L, 1); // QtLua ref
}

QLuaScriptValue QLuaMetaObjectWrapper::create(QLuaScriptEngine * engine, const QMetaObject * mo, bool dynamic)
{
    Q_ASSERT_X(engine != NULL, "QLuaMetaObjectWrapper::create", "Called from incorrect context");
    QLuaScriptEnginePrivate * priv = QLuaScriptEnginePrivate::priv(engine);
    lua_State * L = priv->L;

    void * ptr = lua_newuserdata(L, sizeof(QLuaMetaObjectWrapper));
    if(!ptr) return QLuaScriptValue();

    QLuaMetaObjectWrapper * wrapper = new(ptr) QLuaMetaObjectWrapper();
    wrapper->m_metaObject = mo;
    wrapper->m_dynamic = dynamic;

    lua_getglobal(L, "QtLua");
    lua_getfield(L, -1, "QLuaMetaObjectWrapper__metatable");

    lua_setmetatable(L, -3);

    lua_pop(L, 1); //  QtLua ref

    priv->m_ptrMetaTypes.insert(ptr, qMetaTypeId<QLuaInternal::QLuaMetaObjectWrapper>());

#ifdef QT_DEBUG
    qDebug() << "[QtLua] Created metaobject wrapper for" << mo->className() << "At" << ptr;
#endif

    QLuaScriptValue value(QLuaScriptValue::newFromStack(engine, -1));

    lua_pop(L, 1);

    return value;
}

int QLuaMetaObjectWrapper::dispatch_Gc(lua_State * L)
{
    QLuaScriptEngine * engine = QLuaEngineGlobals::engineFromLua(L);
    Q_ASSERT_X(engine != NULL, "QLuaMetaObjectWrapper::dispatch_Gc", "Called from incorrect context");
    QLuaScriptEnginePrivate * priv = QLuaScriptEnginePrivate::priv(engine);

    void * ptr = lua_touserdata(L, 1);
    int type_id = qMetaTypeId<QLuaInternal::QLuaMetaObjectWrapper>();
    int req_type_id = priv->m_ptrMetaTypes.take(ptr);

    Q_ASSERT_X(req_type_id == type_id, "QLuaMetaObjectWrapper::dispatch_Gc", "Cleaning up incorrent metatype");

    QLuaMetaObjectWrapper * wrapper = (QLuaMetaObjectWrapper *)ptr;

    wrapper->~QLuaMetaObjectWrapper();

#ifdef QT_DEBUG
    qDebug() << "[QtLua] Deleted metaobject wrapper at" << ptr;
#endif

    return 0;
}

int QLuaMetaObjectWrapper::dispatch_Index(lua_State * L)
{
    QLuaScriptEngine * engine = QLuaEngineGlobals::engineFromLua(L);
    Q_ASSERT_X(engine != NULL, "QLuaMetaObjectWrapper::dispatch_Index", "Called from incorrect context");
    QLuaScriptEnginePrivate * priv = QLuaScriptEnginePrivate::priv(engine);

    void * ptr = lua_touserdata(L, 1);
    int type_id = priv->m_ptrMetaTypes.value(ptr, -1);

    Q_ASSERT_X(type_id != -1, "QLuaMetaObjectWrapper::dispatch_Index", "Calling index metamethod on invalid type");

    QLuaMetaObjectWrapper * wrapper = (QLuaMetaObjectWrapper *)ptr;

    size_t len = 0;
    const char * kstr = lua_tolstring(L, 2, &len);

    QByteArray key(kstr, len);

    QLuaScriptValue value(wrapper->index(key));

    if(value.isValid())
    {
        value.pushToStack(engine);
        return 1;
    } else {
        return 0;
    }
}

QLuaScriptValue QLuaMetaObjectWrapper::index(const QByteArray& key)
{
#ifdef QT_DEBUG
    qDebug() << "[QtLua] Index metaobject with key" << key;
#endif
    if(m_enumValues.contains(key))
        return QLuaScriptValue(m_enumValues[key]);

    int enum_count = m_metaObject->enumeratorCount();

    for(int i = enum_count - 1 ; i >= 0 ; --i)
    {
        QMetaEnum menum(m_metaObject->enumerator(i));
        int vv = menum.keyToValue(key.constData());
        if(vv == -1)
            continue;
        m_enumValues.insert(key, vv);
        return QLuaScriptValue(vv);
    }

    return QLuaScriptValue();
}

}
